package org.simple.framework.core.util;

import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.URL;
import java.util.HashSet;
import java.util.Set;

/**
 * @author zhanghaixuan
 * @date 2020-03-05 18:51
 */
@Slf4j
public class ClassUtil {

    public static final String FILE = "file";

    /**
     * 项目类加载器  ClassLoader
     * @param packageName 扫描的包类
     * @return 包下所有资源的集合
     */
    public static Set<Class<?>> extractPackageClass(String packageName) throws IOException {
        final ClassLoader classLoader = getClassLoader();
        packageName = packageName.replace(".","/");
        final URL resource = classLoader.getResource(packageName);

        Set<Class<?>> classSet=null;
        /**
         * 获取本地文件 getProtocol 获取文件协议
         */
        if (resource.getProtocol().equalsIgnoreCase(FILE)) {
            classSet=new HashSet<Class<?>>();

            final File packageDirectory = new File(resource.getPath());

            extractClassFile(classSet,packageDirectory,packageName);

        }

        return  classSet;
    }

    /**
     *
     * @param emptyClassSet 装载目标类的集合
     * @param fileSource  目标类所在的初始文件夹
     * @param packageName  包名
     */
    private static void extractClassFile(Set<Class<?>> emptyClassSet, File fileSource, String packageName) {
        /**
         * 如果不是文件夹就直接退出
         */
        if (!fileSource.isDirectory()) {
            return;
        }
        /**
         * 列出当前路径下的全部文件或者文件夹
         */
        final File[] files = fileSource.listFiles(new FileFilter() {
            @Override
            public boolean accept(File file) {
                if(file.isDirectory()){
                    return true;
                }

                if(file.getAbsolutePath().endsWith(".class")){
                    /**
                     * 若是class文件 直接加载
                     */
                    addClassSet(file.getAbsolutePath());
                }


                return false;
            }

            private void addClassSet(String absolutePath) {
                /**
                 * /Users/zhanghaixuan/workspace/springlearn/simplespring/target/classes/com/zhx/learn/simplespring/HelloServlet.class
                 * 获取到的路径
                 */

                absolutePath = absolutePath.replace(File.separator,".");

                String className = absolutePath.substring(absolutePath.indexOf(packageName.replace("/",".")));
                className = className.replace(".class","");

                /**
                 * 通过反射的方法获取 .class
                 */
                final Class<?> aClass = loadClass(className);
                emptyClassSet.add(aClass);
            }
        });

        if(null != files && files.length >0){
            for (File file : files) {
                extractClassFile(emptyClassSet,file,packageName);
            }
        }
    }

    /**
     * 获取classLoader
     * @return 当前ClassLoader
     */
    public static ClassLoader getClassLoader(){
       return Thread.currentThread().getContextClassLoader();
    }

    public static void main(String[] args) throws IOException {
        extractPackageClass("com.zhx.learn.simplespring");
    }

    /**
     * 通过反射获取className 的 Class
     * @param className class 的名称
     * @return Class
     */
    public static Class<?> loadClass(String className){
        Class<?> aClass = null;
        try {
            aClass = Class.forName(className);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
            log.info("load class error",e);
            throw new RuntimeException("获取异常");
        }

        return aClass;
    }

    /**
     * 实例化class
     */
    public static <T> T newInstance(Class<?> clazz,boolean accessible){
        try {
            Constructor<?> declaredConstructor = clazz.getDeclaredConstructor();
            declaredConstructor.setAccessible(accessible);
            return (T) declaredConstructor.newInstance();
        } catch (Exception e) {
            log.error("转化bean 实例失败,{}",e);
            throw new RuntimeException(e);
        }
    }


    public static void setField(Field field,Object target,Object value,boolean accessible){
        log.info("依赖注入bean:{},{},{}",field,target,value);

        field.setAccessible(accessible);

        try {
            field.set(target,value);
        } catch (IllegalAccessException e) {
            log.error("注入失败: setField");
            throw new RuntimeException(e);
        }
    }
}
